﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Willows.Model
{
	public partial class QueueConnectorReaderInterface 
		: QueueReaderInterface, IQueueConnectorReader
	{

		public override void WriteChannelHierarchy(System.CodeDom.Compiler.IndentedTextWriter textWriter)
		{
			textWriter.WriteLine("Queue connector reader '{0}' parent '{1}'", LocalName, Parent.LocalName);
			textWriter.WriteLine("+---------------------------------------------------------------------+");

			textWriter.WriteLine("Attached Queue: {0}", attachedQueue.LocalName);

			textWriter.WriteLine("Reader(s):");
			textWriter.Indent++;
			foreach (IQueueReaderInterface reader in readers)
				reader.WriteChannelHierarchy(textWriter);
			textWriter.Indent--;
		}

		private List<IQueueReaderInterface> readers;
		public List<IQueueReaderInterface> Reader
		{
			get { return readers; }
		}

		public void BindReader(IQueueReaderInterface reader)
		{
			if (reader == null)
				throw new NullReferenceException("BindReader(): Null argument");

			readers.Add(reader);
		}

		public void BindWriter(IQueueWriterInterface writer)
		{
			throw new NotSupportedException();
		}

		public void UnbindWriter(IQueueWriterInterface writer)
		{
			throw new NotSupportedException();
		}

		public void UnbindReader(IQueueReaderInterface reader)
		{
			if (!this.readers.Contains(reader))
				throw new Exception("UnbindReader(): No such reader.");

			this.readers.Remove(reader);
		}

		public override Module GetWritingDatapath()
		{			
			return AttachedQueue.GetWritingDatapath();
		}

		public override List<Module> GetReadingDatapaths()
		{
			List<Module> result = new List<Module>();

			foreach (IQueueReaderInterface reader in readers)
			{
				System.Diagnostics.Trace.Assert(reader != null);
				result.AddRange(reader.GetReadingDatapaths());
			}

			return result;
		}

		public void DatapathMovedDownInHierarchy(Module newParent, Module oldParent)
		{
			if (AttachedQueue is Queue)
			{
				// cast
				Queue queue = AttachedQueue as Queue;

				// create a new connector reader to insert on the new parent datapath
				// in between the queue and this reader connector

				QueueConnectorReaderInterface newReaderConnector =
					new QueueConnectorReaderInterface(
						newParent, 
						TypeRegistry, 
						Type, 
						UniqueName,
						UniqueName
					);

				newParent.Connectors.Add(newReaderConnector.LocalName, newReaderConnector);

				newReaderConnector.AttachedQueue = queue;
				AttachedQueue = newReaderConnector;
				queue.UnbindReader(this);
				queue.BindReader(newReaderConnector);
				newReaderConnector.BindReader(this);
			}
			else if (AttachedQueue is QueueConnectorReaderInterface)
			{
				throw new NotImplementedException();
			}
			else
			{
				throw new NotSupportedException();
			}
		}

		public void DatapathMovedUpInHierarchy(Module newParent, Module oldParent)
		{
			//  Cannot use for-each directly on the 'readers' list.
			//  The collection may be modified.
			
			if (AttachedQueue is Queue)
			{
				Queue queue = AttachedQueue as Queue;

				QueueConnectorReaderInterface readerConnector =
					new QueueConnectorReaderInterface(
						oldParent, 
						TypeRegistry, 
						Type, 
						UniqueName,
						UniqueName
					);

				queue.UnbindReader(this);
				queue.BindReader(readerConnector);
				readerConnector.AttachedQueue = queue;
				readerConnector.BindReader(this);
				AttachedQueue = readerConnector;
			}
			else if (AttachedQueue is QueueConnectorReaderInterface)
			{
				QueueConnectorReaderInterface readerConnector =
					AttachedQueue as QueueConnectorReaderInterface;

				if (readerConnector.Parent == oldParent)
				{					
					// If the attached queue has the old datapath parent as 
					// its hierarchical parent then the reading end of the 
					// present connector should be moved up.

					readerConnector.UnbindReader(this);
					readerConnector.AttachedQueue.BindReader(this);
					AttachedQueue = readerConnector.AttachedQueue;

					if (readerConnector.readers.Count == 0)
					{
						// there are no readers for this connector -> remove it
						if (!oldParent.Connectors.Remove(readerConnector.LocalName))
							throw new Exception("unable to remove reader connector");

						readerConnector.AttachedQueue.UnbindReader(readerConnector);
					}
				}
				else
				{
					// If the hierarchical parent of the reader connector
					// is not the old parent then we ASSUME that the reader connector
					// belongs to another child datapath of the old parent. In
					// this case we need to insert a new reader connector on the
					// old parent datapath.

					QueueConnectorReaderInterface newReaderConnector =
					  new QueueConnectorReaderInterface(
							oldParent, 
							TypeRegistry, 
							Type,
							UniqueName,
							UniqueName
						);

					newReaderConnector.Parent = oldParent;

					UnbindReader(readerConnector);
					BindReader(newReaderConnector);
					newReaderConnector.AttachedQueue = this;
					newReaderConnector.BindReader(readerConnector);
					readerConnector.AttachedQueue = newReaderConnector;
				}
			}
			else if (AttachedQueue is QueueReaderInterface)
			{
				QueueReaderInterface readerInterface =
					AttachedQueue as QueueReaderInterface;

				QueueConnectorReaderInterface readerConnector =
					new QueueConnectorReaderInterface(
						oldParent, 
						TypeRegistry, 
						Type,
						UniqueName,
						UniqueName
					);

				AttachedQueue = readerConnector;
				readerInterface.AttachedQueue = readerConnector;
				readerConnector.BindReader(readerInterface);
				readerConnector.AttachedQueue = this;
			}
			else
			{
				throw new NotSupportedException();
			}
		}

		#region Constructors
		public QueueConnectorReaderInterface(Module parent, TypeRegistry typeRegistry, Type type, string name, string uniqueName)
			: base(parent, typeRegistry, type, name, uniqueName)
		{			
			readers = new List<IQueueReaderInterface>();
		}

		protected QueueConnectorReaderInterface(Module parent, TypeRegistry typeRegistry, string uniqueName)
			: base(parent, typeRegistry, null, null, uniqueName)
		{
			readers = new List<IQueueReaderInterface>();
		}
		#endregion
	}
}
